//
// ********************************************************************
// * License and Disclaimer                                           *
// *                                                                  *
// * The  Geant4 software  is  copyright of the Copyright Holders  of *
// * the Geant4 Collaboration.  It is provided  under  the terms  and *
// * conditions of the Geant4 Software License,  included in the file *
// * LICENSE and available at  http://cern.ch/geant4/license .  These *
// * include a list of copyright holders.                             *
// *                                                                  *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work  make  any representation or  warranty, express or implied, *
// * regarding  this  software system or assume any liability for its *
// * use.  Please see the license in the file  LICENSE  and URL above *
// * for the full disclaimer and the limitation of liability.         *
// *                                                                  *
// * This  code  implementation is the result of  the  scientific and *
// * technical work of the GEANT4 collaboration.                      *
// * By using,  copying,  modifying or  distributing the software (or *
// * any work based  on the software)  you  agree  to acknowledge its *
// * use  in  resulting  scientific  publications,  and indicate your *
// * acceptance of all terms of the Geant4 Software license.          *
// ********************************************************************
//
//
/// \file optical/wls/src/WLSSteppingAction.cc
/// \brief Implementation of the WLSSteppingAction class
//
//
#include "WLSSteppingAction.hh"

#include "WLSDetectorConstruction.hh"
#include "WLSEventAction.hh"
#include "WLSPhotonDetSD.hh"
#include "WLSSteppingActionMessenger.hh"
#include "WLSUserTrackInformation.hh"

#include "G4ios.hh"
#include "G4OpBoundaryProcess.hh"
#include "G4OpticalPhoton.hh"
#include "G4ProcessManager.hh"
#include "G4Run.hh"
#include "G4SDManager.hh"
#include "G4Step.hh"
#include "G4StepPoint.hh"
#include "G4SystemOfUnits.hh"
#include "G4ThreeVector.hh"
#include "G4Track.hh"
#include "G4TrackStatus.hh"
#include "G4UImanager.hh"
#include "G4VPhysicalVolume.hh"
#include "G4UnitsTable.hh"

// Purpose: Save relevant information into User Track Information

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

WLSSteppingAction::WLSSteppingAction(WLSDetectorConstruction *detector,
                                     WLSEventAction *event)
    : fDetector(detector), fEventAction(event)
{
  fSteppingMessenger = new WLSSteppingActionMessenger(this);
  ResetCounters();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

WLSSteppingAction::~WLSSteppingAction() { delete fSteppingMessenger; }

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void WLSSteppingAction::SetBounceLimit(G4int i) { fBounceLimit = i; }

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4int WLSSteppingAction::GetNumberOfBounces() { return fCounterBounce; }

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4int WLSSteppingAction::GetNumberOfClad1Bounces()
{
  return fCounterClad1Bounce;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4int WLSSteppingAction::GetNumberOfClad2Bounces()
{
  return fCounterClad2Bounce;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4int WLSSteppingAction::GetNumberOfWLSBounces() { return fCounterWLSBounce; }

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4int WLSSteppingAction::ResetSuccessCounter()
{
  G4int temp = fCounterEnd;
  fCounterEnd = 0;
  return temp;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void WLSSteppingAction::UserSteppingAction(const G4Step *theStep)
{
  G4Track *theTrack = theStep->GetTrack();
  auto trackInformation =
      (WLSUserTrackInformation *)theTrack->GetUserInformation();

  G4StepPoint *thePrePoint = theStep->GetPreStepPoint();
  G4StepPoint *thePostPoint = theStep->GetPostStepPoint();

  G4VPhysicalVolume *thePrePV = thePrePoint->GetPhysicalVolume();
  G4VPhysicalVolume *thePostPV = thePostPoint->GetPhysicalVolume();

  G4String thePrePVname = " ";
  G4String thePostPVname = " ";

  if (thePostPV)
  {
    thePrePVname = thePrePV->GetName();
    thePostPVname = thePostPV->GetName();
  }

  // Recording data for start
  // static const G4ThreeVector ZHat = G4ThreeVector(0.0,0.0,1.0);
  if (theTrack->GetParentID() == 0)
  {
    // This is a primary track
    if (theTrack->GetCurrentStepNumber() == 1)
    {
      // G4double x = theTrack->GetVertexPosition().x();
      // G4double y = theTrack->GetVertexPosition().y();
      G4double z = theTrack->GetVertexPosition().z();
      // G4double pz = theTrack->GetVertexMomentumDirection().z();
      // G4double fInitTheta =
      //     theTrack->GetVertexMomentumDirection().angle(ZHat);
      fEventAction->AddFiberZ(z);
    }
  }
  ////////////////////////////////////////////////////////////////////////////////////////////

  // 统计缪子在光纤芯中的能损
  if (theTrack->GetParticleDefinition()->GetParticleName() == "mu+" || theTrack->GetParticleDefinition()->GetParticleName() == "mu-" ||
      theTrack->GetParticleDefinition()->GetParticleName() == "e+" || theTrack->GetParticleDefinition()->GetParticleName() == "e-")
  {
    if (thePrePVname == "WLSFiber")
    {
      fEventAction->AddMuonEdep((theStep->GetTotalEnergyDeposit()) / keV); // 转换单位为keV
    }
  }

  // 不是光子退出
  if (theTrack->GetParticleDefinition()->GetParticleName() != "opticalphoton")
  {
    return;
  }
  // 统计初始光子数
  G4double edep = theTrack->GetKineticEnergy();
  if (theTrack->GetCurrentStepNumber() == 1)
  {
    fEventAction->Addpphoton(); // 累积光子数
  }
  G4double stepLength = 0.;
  if (thePrePVname == "WLSFiber")
  {
    stepLength = theStep->GetStepLength();
    fEventAction->AddAbsCoreL(stepLength);
  }
  if (thePrePVname == "Clad1")
  {
    stepLength = theStep->GetStepLength();
    fEventAction->AddAbsClad1L(stepLength);
  }
  /////////////////////////////////////////////////////////////////////////////////////////
  // Retrieve the status of the photon
  G4OpBoundaryProcessStatus theStatus = Undefined;

  static G4ThreadLocal G4ProcessManager *OpManager =
      G4OpticalPhoton::OpticalPhoton()->GetProcessManager();

  if (OpManager)
  {
    G4int nproc = OpManager->GetPostStepProcessVector()->entries();
    G4ProcessVector *fPostStepDoItVector =
        OpManager->GetPostStepProcessVector(typeDoIt);

    for (G4int i = 0; i < nproc; ++i)
    {
      G4VProcess *fCurrentProcess = (*fPostStepDoItVector)[i];
      fOpProcess = dynamic_cast<G4OpBoundaryProcess *>(fCurrentProcess);
      if (fOpProcess)
      {
        theStatus = fOpProcess->GetStatus();
        break;
      }
    }
  }
  // 杀死光子阻止传输
  // if (theTrack->GetCurrentStepNumber() > 6)
  //   theTrack->SetTrackStatus(fKillTrackAndSecondaries);
  // 设置每个Event打印输出
  // fEventAction->SetEventVerbose(2);

  // 统计光子反射类型
  // 发射光子
  if (theTrack->GetParentID() == 0)
  {
    if (fEventAction->GetInitGamma() == -1 && thePrePVname == "WLSFiber")
    {

      if (theStatus == TotalInternalReflection)
      {
        fEventAction->AddCoreTIR();
        fEventAction->SetInitGamma();
      }
      else if (theStatus == FresnelReflection)
      {
        fEventAction->AddCoreR();
        fEventAction->SetInitGamma();
      }
    }
    if (fEventAction->GetInitGamma() == -1 && thePrePVname == "Clad1")
    {

      if (theStatus == TotalInternalReflection)
      {
        fEventAction->AddClad1TIR();
        fEventAction->SetInitGamma();
      }
      else if (theStatus == FresnelReflection)
      {
        fEventAction->AddClad1R();
        fEventAction->SetInitGamma();
      }
    }
    if (fEventAction->GetInitCoupleR() == -1 && thePrePVname == "ClearFiber" &&
        (theStatus == TotalInternalReflection || theStatus == FresnelReflection))
    {
      fEventAction->AddCoupleR();
      fEventAction->SetInitCoupleR();
    }
  }
  else // 发射缪子
  {
    if (trackInformation->GetInitGamma() == -1 && thePrePVname == "WLSFiber")
    {

      if (theStatus == TotalInternalReflection)
      {
        fEventAction->AddCoreTIR();
        trackInformation->SetInitGamma();
      }
      else if (theStatus == FresnelReflection)
      {
        fEventAction->AddCoreR();
        trackInformation->SetInitGamma();
      }
    }
    if (trackInformation->GetInitGamma() == -1 && thePrePVname == "Clad1")
    {

      if (theStatus == TotalInternalReflection)
      {
        fEventAction->AddClad1TIR();
        trackInformation->SetInitGamma();
      }
      else if (theStatus == FresnelReflection)
      {
        fEventAction->AddClad1R();
        trackInformation->SetInitGamma();
      }
    }
    if (trackInformation->GetInitCoupleR() == -1 && thePrePVname == "ClearFiber" &&
        (theStatus == TotalInternalReflection || theStatus == FresnelReflection))
    {
      fEventAction->AddCoupleR();
      trackInformation->SetInitCoupleR();
    }
  }
  // Find the skewness of the ray at first change of boundary
  if (fInitGamma == -1 &&
      (theStatus == TotalInternalReflection || theStatus == FresnelReflection ||
       theStatus == FresnelRefraction) &&
      trackInformation->IsStatus(InsideOfFiber))
  {
    G4double px = theTrack->GetVertexMomentumDirection().x();
    G4double py = theTrack->GetVertexMomentumDirection().y();
    G4double x = theTrack->GetPosition().x();
    G4double y = theTrack->GetPosition().y();

    fInitGamma = x * px + y * py;

    fInitGamma =
        fInitGamma / std::sqrt(px * px + py * py) / std::sqrt(x * x + y * y);

    fInitGamma = std::acos(fInitGamma * rad);

    if (fInitGamma / deg > 90.0)
    {
      fInitGamma = 180 * deg - fInitGamma;
    }
  }
  // Record Photons that missed the photon detector but escaped from readout
  if (!thePostPV && trackInformation->IsStatus(EscapedFromReadOut))
  {
    // G4cout << "SteppingAction: status = EscapedFromReadOut" << G4endl;
    fEventAction->AddEscaped();
    // UpdateHistogramSuccess(thePostPoint,theTrack);
    ResetCounters();

    return;
  }

  // Assumed photons are originated at the fiber OR
  // the fiber is the first material the photon hits
  switch (theStatus)
  {
  // Exiting the fiber
  case FresnelRefraction:
  case SameMaterial:
    fEventAction->AddExiting();

    if (thePostPVname == "WLSFiber" || thePostPVname == "Clad1" ||
        thePostPVname == "Clad2")
    {
      if (trackInformation->IsStatus(OutsideOfFiber))
        trackInformation->AddStatusFlag(InsideOfFiber);

      // Set the Exit flag when the photon refracted out of the fiber
    }
    else if (trackInformation->IsStatus(InsideOfFiber))
    {
      // EscapedFromReadOut if the z position is the same as fiber's end
      if (theTrack->GetPosition().z() == fDetector->GetWLSFiberEnd() || theTrack->GetPosition().z() == -1 * fDetector->GetWLSFiberEnd())
      {
        trackInformation->AddStatusFlag(EscapedFromReadOut);
        fCounterEnd++;
        fEventAction->AddEscapedEnd();
      }
      else // Escaped from side
      {
        trackInformation->AddStatusFlag(EscapedFromSide);
        trackInformation->SetExitPosition(theTrack->GetPosition());
        //  UpdateHistogramEscape(thePostPoint,theTrack);

        fCounterMid++;
        fEventAction->AddEscapedMid();
        ResetCounters();
      }

      trackInformation->AddStatusFlag(OutsideOfFiber);
      trackInformation->SetExitPosition(theTrack->GetPosition());
    }

    return;

  // Internal Reflections
  case TotalInternalReflection:

    fEventAction->AddTIR();

    // Kill the track if it's number of bounces exceeded the limit
    if (fBounceLimit > 0 && fCounterBounce >= fBounceLimit)
    {
      theTrack->SetTrackStatus(fStopAndKill);
      trackInformation->AddStatusFlag(murderee);
      ResetCounters();
      G4cout << "\n Bounce Limit Exceeded" << G4endl;
      return;
    }
    break;

  case FresnelReflection:

    fCounterBounce++;
    fEventAction->AddBounce();

    if (thePrePVname == "WLSFiber")
    {
      fCounterWLSBounce++;
      fEventAction->AddWLSBounce();
    }
    else if (thePrePVname == "Clad1")
    {
      fCounterClad1Bounce++;
      fEventAction->AddClad1Bounce();
    }
    else if (thePrePVname == "Clad2")
    {
      fCounterClad2Bounce++;
      fEventAction->AddClad1Bounce();
    }

    // Determine if the photon has reflected off the read-out end
    if (theTrack->GetPosition().z() == fDetector->GetWLSFiberEnd() || theTrack->GetPosition().z() == -1 * fDetector->GetWLSFiberEnd())
    {
      if (!trackInformation->IsStatus(ReflectedAtReadOut) &&
          trackInformation->IsStatus(InsideOfFiber))
      {
        trackInformation->AddStatusFlag(ReflectedAtReadOut);

        if (fDetector->IsPerfectFiber() &&
            theStatus == TotalInternalReflection)
        {
          theTrack->SetTrackStatus(fStopAndKill);
          trackInformation->AddStatusFlag(murderee);
          // UpdateHistogramReflect(thePostPoint,theTrack);
          ResetCounters();
          return;
        }
      }
    }
    return;

  // Reflection off the mirror
  case LambertianReflection:
  case LobeReflection:
  case SpikeReflection:

    fEventAction->AddReflected();
    // Check if it hits the mirror
    if (thePostPVname == "Mirror")
    {
      trackInformation->AddStatusFlag(ReflectedAtMirror);
      fEventAction->AddMirror();
    }
    return;

  // Detected by a detector
  case Detection:
    // Detected automatically with G4OpBoundaryProcess->InvokeSD set true

    // Stop Tracking when it hits the detector's surface
    ResetCounters();
    theTrack->SetTrackStatus(fStopAndKill);
    return;

  default:
    break;
  }

  // Check for absorbed photons
  if (theTrack->GetTrackStatus() != fAlive &&
      trackInformation->IsStatus(InsideOfFiber))
  {
    // UpdateHistogramAbsorb(thePostPoint,theTrack);
    ResetCounters();
    return;
  }
}
